package dao

import (
	"context"
	"fmt"
	"gitee.com/hxchjm/go-admin/internal/models"
	"gitee.com/hxchjm/go-admin/pkg/cas"
	"gitee.com/hxchjm/go-admin/pkg/db"
	"gitee.com/hxchjm/go-admin/pkg/jwtauth"
	"github.com/hxchjm/log"
	"github.com/jinzhu/copier"
	"github.com/pkg/errors"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

type SysRole struct {
	models.SysRole
}

//func (*SysRole) TableName() string {
//	return "sys_role"
//	//return fmt.Sprintf("%s.sys_role", db.GetDBName())
//}

func (e SysRole) GetList(ctx context.Context, req *models.ReqRoleGetPage) ([]SysRole, int64, error) {
	var list []SysRole
	var count int64
	if req.Size == 0 {
		req.Size = 20
	}
	if err := db.GetDB().Model(SysRole{}).Preload("SysMenu").Where(req).Limit(req.Size).Offset((req.Page - 1) * req.Size).
		Find(&list).Error; err != nil {
		log.Errorf(ctx, "SysRole GetList err (%+v)", err)
		return nil, 0, err
	}
	db.GetDB().Model(SysRole{}).Count(&count)
	return list, count, nil
}

func (e SysRole) Get(ctx context.Context) (*SysRole, error) {
	var role SysRole
	err := db.GetDB().Model(e).Where(e).First(&role).Error
	if err != nil {
		log.Errorf(ctx, "SysRole Get err (%+v)", err)
		return nil, err
	}
	role.MenuIds, err = e.GetRoleMenuId(e.RoleId)
	if err != nil {
		log.Errorf(ctx, "GetRoleMenuId Get err (%+v)", err)
		return nil, err
	}
	return &role, nil
}

// Insert 根据menu_id在表sys_menu中找到
func (e SysRole) Insert(ctx context.Context) error {
	//1.根据menu_id在sys_menu中找到菜单项以及外键关联的sys_api
	var dataMenu []models.SysMenu
	if err := db.GetDB().Debug().Preload("SysApi").Where("menu_id in ?", e.MenuIds).First(&dataMenu).Error; err != nil {
		log.Errorf(ctx, "SysRole Insert err (%+v)", err)
		return err
	}
	e.SysMenu = &dataMenu

	var count int64
	if err := db.GetDB().Debug().Model(e).Where("role_key = ?", e.RoleKey).Count(&count).Error; err != nil {
		log.Errorf(ctx, "db error:%s", err)
		return err
	}

	if count > 0 {
		log.Error(ctx, "roleKey已存在，需更换在提交！")
		return fmt.Errorf("roleKey已存在，需更换在提交！")
	}
	e.CreateBy = jwtauth.GetClaims(ctx).UID
	e.UpdateBy = jwtauth.GetClaims(ctx).UID
	if err := db.GetDB().Debug().Create(&e).Error; err != nil {
		log.Errorf(ctx, "SysRole create err (%+v)", err)
		return err
	}
	mp := make(map[string]struct{}, 0)
	polices := make([][]string, 0)
	for _, menu := range dataMenu {
		for _, api := range menu.SysApi {
			if _, ok := mp[e.RoleKey+"-"+api.Path+"-"+api.Action]; !ok {
				mp[e.RoleKey+"-"+api.Path+"-"+api.Action] = struct{}{}
				polices = append(polices, []string{e.RoleKey, api.Path, api.Action})
			}
		}
	}

	if len(polices) <= 0 {
		return nil
	}

	// 写入 sys_casbin_rule 权限表里 当前角色数据的记录
	if _, err := cas.GetIns().AddNamedPolicies("p", polices); err != nil {
		return err
	}

	return nil
}

func (e SysRole) Update(ctx context.Context) error {
	var err error
	tx := db.GetDB().Debug().Begin()

	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()

	var model = models.SysRole{}
	var mlist = make([]models.SysMenu, 0)
	tx.Preload("SysMenu").First(&model, e.RoleId)
	tx.Preload("SysApi").Where("menu_id in ?", e.MenuIds).Find(&mlist)
	err = tx.Model(&model).Association("SysMenu").Delete(model.SysMenu)
	if err != nil {
		log.Errorf(ctx, "delete policy error:%s", err)
		return err
	}
	_ = copier.CopyWithOption(&model, e, copier.Option{IgnoreEmpty: true})
	//e.SysRole.SysMenu = &mlist

	model.SysMenu = &mlist
	model.UpdateBy = jwtauth.GetClaims(ctx).UID
	// 更新关联的数据，使用 FullSaveAssociations 模式
	tx2 := tx.Session(&gorm.Session{FullSaveAssociations: true}).Debug().Save(&model)
	if err = tx2.Error; err != nil {
		log.Errorf(ctx, "db error:%s", err)
		return err
	}
	if tx2.RowsAffected == 0 {
		return fmt.Errorf("无权更新该数据")
	}

	// 清除 sys_casbin_rule 权限表里 当前角色的所有记录
	_, err = cas.GetIns().RemoveFilteredPolicy(0, model.RoleKey)
	if err != nil {
		log.Errorf("delete policy error:%s", err)
		return err
	}
	mp := make(map[string]struct{}, 0)
	polices := make([][]string, 0)
	for _, menu := range mlist {
		for _, api := range menu.SysApi {
			if _, ok := mp[model.RoleKey+"-"+api.Path+"-"+api.Action]; !ok {
				mp[model.RoleKey+"-"+api.Path+"-"+api.Action] = struct{}{}
				//_, err = cb.AddNamedPolicy("p", model.RoleKey, api.Path, api.Action)
				polices = append(polices, []string{model.RoleKey, api.Path, api.Action})
			}
		}
	}
	if len(polices) <= 0 {
		return nil
	}

	// 写入 sys_casbin_rule 权限表里 当前角色数据的记录
	if _, err = cas.GetIns().AddNamedPolicies("p", polices); err != nil {
		return err
	}
	return nil
}

// Delete 删除SysRole
func (e SysRole) Delete(ctx context.Context, ids []int) error {
	var err error
	tx := db.GetDB().Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()
	var model = models.SysRole{}
	tx.Preload("SysMenu").Preload("SysDept").First(&model, ids)
	//删除 SysRole 时，同时删除角色所有 关联其它表 记录 (SysMenu 和 SysMenu)
	tx = tx.Select(clause.Associations).Delete(&model)

	if err = tx.Error; err != nil {
		log.Errorf(ctx, "db error:%s", err)
		return err
	}
	if tx.RowsAffected == 0 {
		return fmt.Errorf("无权更新该数据")
	}

	// 清除 sys_casbin_rule 权限表里 当前角色的所有记录
	_, _ = cas.GetIns().RemoveFilteredPolicy(0, model.RoleKey)

	return nil
}

// GetRoleMenuId 获取角色对应的菜单ids
func (e SysRole) GetRoleMenuId(roleId int) ([]int, error) {
	menuIds := make([]int, 0)
	model := SysRole{}
	model.RoleId = roleId
	if err := db.GetDB().Model(&model).Preload("SysMenu").First(&model).Error; err != nil {
		return nil, err
	}
	l := *model.SysMenu
	for i := 0; i < len(l); i++ {
		menuIds = append(menuIds, l[i].MenuId)
	}
	return menuIds, nil
}

func (e SysRole) GetByKey(ctx context.Context) ([]string, error) {
	permissions := make([]string, 0)
	model := models.SysRole{}
	//model.RoleKey = e.RoleKey
	if err := db.GetDB().Debug().Model(&model).Preload("SysMenu").First(&model, e).Error; err != nil {
		return nil, err
	}
	l := *model.SysMenu
	for i := 0; i < len(l); i++ {
		if l[i].Permission != "" {
			permissions = append(permissions, l[i].Permission)
		}
	}
	return permissions, nil
}

func (e SysRole) UpdateDataScope(ctx context.Context) error {
	var err error
	tx := db.GetDB().WithContext(ctx).Debug().Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()
	var dlist = make([]models.SysDept, 0)
	var model = models.SysRole{}
	tx.Preload("SysDept").First(&model, e.RoleId)
	tx.Where("dept_id in ?", e.DeptIds).Find(&dlist)
	// 删除SysRole 和 SysDept 的关联关系
	err = tx.Model(&model).Association("SysDept").Delete(model.SysDept)
	if err != nil {
		log.Errorf(ctx, "delete SysDept error:%s", err)
		return err
	}
	_ = copier.CopyWithOption(&model, e, copier.Option{IgnoreEmpty: true})
	model.SysDept = dlist
	model.UpdateBy = jwtauth.GetClaims(ctx).UID
	// 更新关联的数据，使用 FullSaveAssociations 模式
	tx = tx.Model(&model).Session(&gorm.Session{FullSaveAssociations: true}).Debug().Save(&model)
	if err = tx.Error; err != nil {
		log.Errorf(ctx, "db error:%s", err)
		return err
	}
	if tx.RowsAffected == 0 {
		return errors.New("无权更新该数据")
	}
	return nil
}
